﻿// 文件使用MiniExcel源码， 使用时需遵循其 Apache License 2.0
// 修改摘要通知： 删除异步、Task等net2.0中不支持的内容，替换原先C#自带的zip处理方式，改用yacomp 进行解压缩。
using ExcelConfig.Utils;
using ExcelConfig.WriteAdapter;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

namespace ExcelConfig.Csv
{
    internal class CsvWriter : IExcelWriter, IDisposable
    {
        private readonly StreamWriter _writer;
        private readonly CsvConfiguration _configuration;
        private readonly bool _printHeader;
        private readonly object _value;
        private bool _disposedValue;

        public CsvWriter(Stream stream, object value, IConfiguration configuration, bool printHeader)
        {
            _configuration = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration;
            _printHeader = printHeader;
            _value = value;
            _writer = _configuration.StreamWriterFunc(stream);
        }

        public int[] SaveAs()
        {
            if (_value == null)
            {
                _writer.Write("");
                _writer.Flush();
                return new int[0];
            }

            var rowsWritten = WriteValues(_value);
            _writer.Flush();

            return new[] { rowsWritten };
        }

        public int Insert(bool overwriteSheet = false)
        {
            var v = SaveAs();
            if (v.Length == 0) return default;
            return v[0];
        }

        private void AppendColumn(StringBuilder rowBuilder, CellWriteInfo column)
        {
            rowBuilder.Append(CsvHelpers.ConvertToCsvValue(ToCsvString(column.Value, column.Prop), _configuration.AlwaysQuote, _configuration.Seperator));
            rowBuilder.Append(_configuration.Seperator);
        }

        private static void RemoveTrailingSeparator(StringBuilder rowBuilder)
        {
            if (rowBuilder.Length == 0)
                return;
            
            rowBuilder.Remove(rowBuilder.Length - 1, 1);
        }

        private string GetHeader(List<ExcelColumnInfo> props)
        {
            var props_ = new List<string>();
            for (var i=0; i < props.Count; i++) props_[i] = CsvHelpers.ConvertToCsvValue(props[i]?.ExcelColumnName, _configuration.AlwaysQuote, _configuration.Seperator);
            return string.Join(_configuration.Seperator.ToString(), props_.ToArray());
        }

        private int WriteValues(object values)
        {
            var writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);

            var props = writeAdapter.GetColumns();
            if (props == null)
            {
                _writer.Write(_configuration.NewLine);
                _writer.Flush();
                return 0;
            }

            if (_printHeader)
            {
                _writer.Write(GetHeader(props));
                _writer.Write(_configuration.NewLine);
            }

            if (writeAdapter == null) 
                return 0;
            
            var rowBuilder = new StringBuilder();
            var rowsWritten = 0;

            foreach (var row in writeAdapter.GetRows(props))
            {
                rowBuilder.Remove(0, rowBuilder.Length);
                foreach (var column in row)
                {
                    AppendColumn(rowBuilder, column);
                }
                RemoveTrailingSeparator(rowBuilder);
                _writer.Write(rowBuilder.ToString());
                _writer.Write(_configuration.NewLine);
                    
                rowsWritten++;
            }
            return rowsWritten;
        }
        public string ToCsvString(object value, ExcelColumnInfo p)
        {
            if (value == null)
                return "";

            if (value is DateTime dateTime)
            {
                if (p?.ExcelFormat != null)
                {
                    return dateTime.ToString(p.ExcelFormat, _configuration.Culture);
                }
                return _configuration.Culture.Equals(CultureInfo.InvariantCulture) 
                    ? dateTime.ToString("yyyy-MM-dd HH:mm:ss", _configuration.Culture) 
                    : dateTime.ToString(_configuration.Culture);
            }
            
            if (p?.ExcelFormat != null && value is IFormattable formattableValue)
                return formattableValue.ToString(p.ExcelFormat, _configuration.Culture);

            return Convert.ToString(value, _configuration.Culture);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                {
                    _writer.Dispose();
                    // TODO: dispose managed state (managed objects)
                }

                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
                // TODO: set large fields to null
                _disposedValue = true;
            }
        }

        // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
        ~CsvWriter()
        {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: false);
        }

        public void Dispose()
        {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }
}